<?php

/*
 * core/log.php
 *
 * Handle all type of logs
 */

/*
 * ErrorHandler class
 * Methods called when an error is emited
 * This allow other system to get a callback when an error occured
 * lock()/unlock() methods must be called and tested before calling log(), its used avoid recursive calls.
 */
abstract class LogHandler
{
	private $isInner = false;

	private function lock()
	{
		if( $this->isInner ) return false;
		$this->isInner = true;
		return true;
	}

	private function unlock()
	{
		if( !$this->isInner ) die( 'ErrorHandler::unlock: not locked!' );
		$this->isInner = false;
	}

	public function report( $type, $msg, $className=null, $methodName=null, $fileName=null, $lineNo=null )
	{
		if( !$this->lock() ) return;
		$this->log( $type, $msg, $className, $methodName, $fileName, $lineNo );
		$this->unlock();
	}

	protected abstract function log( $type, $msg, $className=null, $methodName=null, $fileName=null, $lineNo=null );
};

/*
 * Log Type defines
 */
define( 'T_DEBUG', 0 );
define( 'T_INFO',  1 );
define( 'T_ERROR', 2 );
define( 'T_FATAL', 3 );

/*
 * Log class
 */
class Log
{
	private static $instance = null;

	private $logs = array();

	private $handlers = array();

	private function __construct()
	{
	}

	public static function &inst()
	{
		return Log::$instance;
	}

	public static function create()
	{
		if( is_null(Log::$instance) )
		{
			Log::$instance = new Log();
		}
	}

	public static function debug( $msg )
	{
		Log::$instance->addLog( T_DEBUG, $msg );
	}

	public static function info( $msg )
	{
		Log::$instance->addLog( T_INFO, $msg );
	}

	public static function error( $msg )
	{
		Log::$instance->addLog( T_ERROR, $msg );
	}

	public static function fatal( $msg )
	{
		Log::$instance->addLog( T_FATAL, $msg );
	}

	public static function death( $msg )
	{
		Log::$instance->addLog( T_FATAL, $msg );
		Log::$instance->printLogs( true );
	}

	public static function backtrace( $msg=null )
	{
		Log::$instance->addBacktrace( $msg );
	}

	public static function registerHandler( $handler )
	{
		$_this =& Log::$instance;
		if( in_array( $handler, $_this->handlers ) )
			return;
		array_push( $_this->handlers, $handler );
	}
	
	public static function deregisterHandler( $handler )
	{
		$_this =& Log::$instance;
		if( !in_array( $handler, $_this->handlers ) )
			return;
		$i = sizeof($_this->handlers);
		while( $i-- )
		{
			if( $_this->handlers[$i] == $handler )
				unset( $_this->handlers[$i] );
		}
	}

	public static function clearHandlers()
	{
		Log::$instance->handlers = array();
	}

	public static function logTypeToStr( $ty )
	{
		static $types = array( T_DEBUG=>'DEBUG', T_INFO=>'INFO', T_ERROR=>'ERROR', T_FATAL=>'FATAL' );
		if( !array_key_exists($ty, $types) )
			return 'UNKNOWN TYPE';
		return $types[$ty];
	}

	public function getLogs()
	{
		if( sizeof($this->logs) == 0 )
			return null;
		return $this->logs;
	}

	private function printLogs( $andDie=false )
	{
		$out = '<tt>'."\n";
		$count = sizeof($this->logs);
		$i = 0;
		while( $count-- )
		{
			$l =& $this->logs[$i++];
			$ty = Log::logTypeToStr( $l['type'] );
			$fl = ($l['file']=='') ? '' : '('.$l['file'].', '.$l['line'].')';
			$out .= sprintf( '<b>[%5s]</b> - %s::%s: <u>%s</u> <font size="-1">%s</font> <br>'."\n", $ty, $l['class'], $l['mth'], $l['msg'], $fl );
		}
		$out .= '</tt>';

		if( $andDie )
			die( $out );
		else
			print( $out );
	}

	private function addLog( $type, $msg, $forceNoDebug=false )
	{
		// if we have debug info, we'll get 'class', 'mth', 'file' and 'line' entries in plus
		$l = array( 'ty'=>$type, 'msg'=>$msg, 'fl'=>null );

		if( !$forceNoDebug && Conf::$c['debug']['active'] )
		{
			$bt = Util::getBacktrace();
			// sizeof-1: start from 0; -1: not getBacktrace(); -2: not addLog(), and  not Log::debu/Log::info...etc.
			$i = (sizeof($bt)-1) -1 -2;
			if( $i < 0 ) $i = 0;
			$l['cl'] = $bt[$i]['class'];
			$l['mth'] = $bt[$i]['mth'];
			$l['fl'] = $bt[$i]['file'];
			$l['li'] = $bt[$i]['line'];
		}

		array_push( $this->logs, $l );

		foreach( $this->handlers as $h )
		{
			$h->report( $type, $msg, $class, $method, $file, $line );
		}
	}

	private function addBacktrace( $msg )
	{
		$bt = Util::getBacktrace();

		$this->addLog( T_DEBUG, 'Backtrace '.(is_null($msg))?'':'('.$msg.') '.':' );
		for( $i = 0; $i<sizeof($bt); $i++ )
		{
			$b =& $bt[$i];
			$fileline = (is_null($b['file'])) ? '' : '('.$b['file'].', '.$b['line'].')';
			$str = '['.$i.'] '.$b['class'].'::'.$b['mth'].' - '.$fileline;
			$this->addLog( T_DEBUG, $str, true );
		}

	}
};

Log::create();

?>
